/**********************************************************************/
/**  Copyright (c) ECC labs. 2001-2002. All Rights Reserved.		 **/
/**********************************************************************/
/*
	bincode.cpp
	Mark Papazyan

*/

#include "bincode.h"


unsigned long *C[33];


CBinCode::CBinCode(t_len k, t_len n, int flag, int base)
	:CBinSpace(k, n, flag, base), d(0)
{	

	if(flag & HAVE_A)
		A=new t_len[n+1];
}


CBinCode::CBinCode(const CBinCode& t)
{
	row=new CBinWord[k=t.k];
	col=new CBinWord[n=t.n];

	flag=t.flag;
	base=t.base;

	for(t_cnt i=0; i<k; i++)
	{
		row[i].num=t.row[i].num;
		row[i].n=t.n;
	}
	
	for(t_cnt ii=0; ii<n; ii++)
		col[ii].n=t.k;

	if(t.flag&HAVE_COL)//checks if cols were made
		for(t_cnt j=0; j<n; j++)
			col[j].num=t.col[j].num;

	if(flag & PERM)
	{
			/*series=new t_tab[t.n];

		for(t_cnt iii=0; iii<t.n ;iii++)
			series[iii]=t_tab(iii);*/
		dat.s = new int[n] ;
		dat.flag = new long[n];
		pos = pos1 = 0 ;
		for ( t_cnt i=0; i<n; i++ ) {
			dat.s[i]=t.dat.s[i] ;
			dat.flag[i]=t.dat.flag[i] ;
		}
	}

	d=t.d;

	if(t.flag & HAVE_A)
	{
		A=new t_len[t.n+1];//if A exists
		for(t_cnt h=0; h<=n; h++)
			A[h]=t.A[h];
	}
}




CBinCode::~CBinCode()
{
	if(flag & HAVE_A)
		delete[] A;
}


ostream& operator<<(ostream& out, CBinCode& a)
{
	return operator<<(out, (CBinMatrix&)a);
}



istream& operator>>(istream& in, CBinCode& a)
{
	return operator>>(in, (CBinMatrix&)a);
}



CBinCode& CBinCode::operator=(const CBinCode& t)
{
	if(this!=&t)
	{
		CBinMatrix::operator =(t);
		
		if(flag&HAVE_A)
			delete[] A;

		if(flag & PERM){
			delete[] dat.flag;
			delete[] dat.s;
		}
		
		d=t.d;

		if(t.flag & HAVE_A)	//if A exists
		{
			A=new t_len[n+1];
			for(t_cnt h=0; h<=n; h++)
				A[h]=t.A[h];
		}

		if(t.flag & PERM){
			if(t.flag & PERM)
		{
			dat.s = new int[n] ;
			dat.flag = new long[n];
			pos = pos1 = 0 ;
			for ( t_cnt i=0; i<n; i++ ) {
				dat.s[i]=t.dat.s[i] ;
				dat.flag[i]=t.dat.flag[i] ;
			}
		}
		}
	}

	return *this;
}




void CBinCode::h1_to_h()
{
	n=k+n;
	for(t_cnt i=0; i<k; i++)
		row[i].num=(row[i].num<<k)+(I<<(k-1-i));

	flag^=IS_H1^IS_H;
	
}


void CBinCode::g1_to_g()
{
	trans();

	for(t_cnt j=0;j<n;j++)
		row[j].num=row[j].num+(I<<(n+k-j-1));
	t_len tmp=n;
	n=k+n;
	k=tmp;

	flag^=IS_G^IS_G1;
}




void CBinCode::h_to_h1()
{
	for(t_cnt i=0; i<k; i++)
		row[i].num>>=k;
	n=n-k;

	flag^=IS_H1^IS_H;
}




void CBinCode::g_to_g1()
{	
	trans();

	t_len t=k;
	k=n-k;
	n=t;

	flag^=IS_G1^IS_G;

}
/*
void CBinCode::convert()
{
	if(n==k)
		return;
	CBinCode Res(n-k,n);

	CBinWord check(n,(I)0);	//two vectors dim=n: vector of primary values
	CBinWord uncheck(n,(I)0);	//vector of free values
	CBinWord temp(n,(I)0);	//here we'll count our results
	CBinWord pos(k, (I)0);	//vector that indicates those rows where vector of primary values are
	CBinWord some(n, (I)0);
	t_len s;

	check=gaus();
	uncheck.num=~check.num;	//switching vector of free values

	t_len h=0;
	s=0;
	
	for(t_cnt i=0; i<n-k; i++)
	{
		while(!((uncheck.num>>s)&I))s++;	//finding first 1 in a vector of free values
		temp.num=check.num^(I<<s);	//switching on free value 1 position
		s++;

		for(t_cnt f=k-1; f>=0; f--)
		{
				while(!(check.num>>h&I))h++;	//finding 1 in a vector of primary values
			
				some.num=row[f].num&temp.num;
		
				temp.num^=((some.wt()%2))<<h;	//switch on position from primary	
				
				h++;
		}

		Res.row[i].num=temp.num;
		h=0;
	}
	
	*this=Res;


	set_h();
}

*/
void CBinCode::spec()
{
	
	CBinWord tmp(n, (t_src)0);
	t_src foo=(t_src)0;
	t_len temp;
	d=n;	//by default d is maximal

	if(!(flag&HAVE_A)){
		A=new t_len[n+1];
		flag^=HAVE_A;
	}

	A[0]=1;
	for(t_cnt h=1; h<=n; h++)//annulating
		A[h]=0;

	for(t_src i=1; i<(I<<k); i++)// 1<<k is an amount of code words
	{
		foo=(t_src)0;
		tmp.num=(t_src)0;
		while(foo<(unsigned)k)
		{	//summation of all combination of vectors
			if(i>>foo &I)tmp.num^=row[foo].num;	//making all possible combinations
			foo++;
		}
		temp=tmp.wt();

		A[temp]++;
		(temp<d)?d=temp:d;
	}

	flag^=HAVE_D;//indicates that A & d were made
}




//note that a and b must be G matrix


t_len CBinCode::d1()
{
	Mac_Wil();
	A[0]--;
	for(t_cnt i=0; i<=n; i++)
		if(A[i]!=0){
			d=i;
			break;
		}
	return d;
}


t_len CBinCode::d2()
{	
	CBinWord tmp(n, (t_src)0);
	t_src foo=(t_src)0;
	t_len temp;
	d=n;

	for(t_src i=1; i<(I<<k); i++)// 1<<k is an amount of code words
	{
		foo=(t_src)0;
		tmp.num=(t_src)0;
		while(foo<(unsigned)k)
		{	//summation of all combination of vectors
			if(i>>foo &I)tmp.num^=row[foo].num;	//making all possible combinations
			foo++;
		}
		temp=tmp.wt();
		(temp<d)?d=temp : d;
	}

	flag^=HAVE_D;//indicates that A & d were made

	return d;
}


t_len CBinCode::get_d()
{
	if(k>(n/2))
		return d1();
	return d2();
}


float c_n_k(t_len  n, t_len k)
{
	float temp=1;
	if(k<(n-k))
		for(t_cnt i=0; i<k; i++)
		{
			temp*=(n-k+i+1);
			temp/=(i+1);
		}

	else
		for(t_cnt i=0; i<n-k; i++)
		{
			temp*=(k+i+1);
			temp/=(i+1);
		}

		return temp;

}



CBinCode& CBinCode::operator =(CBinMatrix&t)
{
	if(this!=&t)
	{
		delete[] row;
		delete[] col;
		
		
		if(flag& PERM){
			delete[] dat.flag;
			delete[] dat.s;
		}

		row=new CBinWord[k=t.k];
		col=new CBinWord[n=t.n];

		d=n;

		flag=t.flag;
		base=t.base;

		for(t_cnt i=0; i<k; i++)
		{
			row[i].num=t.row[i].num;
			row[i].n=n;
		}

		for(t_cnt ii=0; ii<n; ii++)
			col[ii].n=k;

		if(t.flag & HAVE_COL)//checks if cols were made
			for(t_cnt j=0; j<n; j++)
				col[j].num=t.col[j].num;

		if(t.flag & PERM)
		{
			dat.s = new int[n] ;
			dat.flag = new long[n];
			pos = pos1 = 0 ;
			for ( t_cnt i=0; i<n; i++ ) {
				dat.s[i]=t.dat.s[i] ;
				dat.flag[i]=t.dat.flag[i] ;
			}
		}

	}

	return *this;
}



CBinCode::CBinCode(const CBinMatrix& t)
{
	row=new CBinWord[k=t.k] ;
	col=new CBinWord[n=t.n] ;

	flag=t.flag;
	base=t.base;
	d=n;

	for(t_cnt i=0; i<k; i++)
	{
		row[i].num=t.row[i].num;
		row[i].n=t.n;
	}
	
	for(t_cnt ii=0; ii<n; ii++)
		col[ii].n=t.k;

	if(t.flag&HAVE_COL)//checks if cols were made
		for(t_cnt j=0; j<n; j++)
			col[j].num=t.col[j].num;

	if(t.flag & PERM)
	{
			/*series=new t_tab[t.n];

		for(t_cnt iii=0; iii<t.n ;iii++)
			series[iii]=t_tab(iii);*/
		dat.s = new int[n] ;
		dat.flag = new long[n];
		pos = pos1 = 0 ;
		for ( t_cnt i=0; i<n; i++ ) {
			dat.s[i]=t.dat.s[i] ;
			dat.flag[i]=t.dat.flag[i] ;
		}
	}
}



t_src c_n_k_2(t_len n, t_len k)
{
	if(n==k || k==0)
		return I;
	if(n<k)
		return (t_src)0;

    t_src* m1=new t_src[k];
    t_src* m2=new t_src[k-1];
    t_src res=I;
    

    for(t_cnt i=0; i<k-1; i++)
	{
		m1[i]=(t_src)n-i;
		m2[i]=(t_src)i+2;
    }

    m1[k-1]=(t_src)n-k+1;
    
    for(t_cnt j=k-2; j>=0; j--)
    {
		for(t_cnt u=0; u<k; u++)
		{
			if((t_src)m1[u]%m2[j]==(t_src)0)
			{
				m1[u]=(t_src)(m1[u]/m2[j]);
				break;
			}
		}
    }
    
    for(t_cnt u=0; u<k; u++)
        res*=(t_src)m1[u];
	
    return res;
}



void c_init() {

	static bool have=0;
	if(!have){
	for ( int i=0; i<33; i++ )	{
		C[i] = new unsigned long[i+1] ;
		C[i][0] = C[i][i] = 1 ;
		for ( int k=1; k<i; k++ ) {
			C[i][k] = C[i-1][k-1] + C[i-1][k] ;
		}
	}
		have=1;
	}
} 



void CBinCode::Mac_Wil() {

	CBinCode tmp=*this;
// нужно сразу spec, потом преобразовать по формуле и возвращать преобразованный
// спектр, указатель на который поступает в качестве аргумента ... 
// ЕЩЕ РАЗ НАПОМНЮ, ЧТО НУЖНО ПОПЫТАТЬСЯ ОБОЙТИСЬ БЕЗ ВЫЗОВА ТЯЖЕЛЫХ 
// ФУНКЦИЙ И КОНСТРУКТОРОВ НА НИЖНЕМ УРОВНЕ класса bincode -- 
// эти вызовы нужно поднять в соответствующие ф-ции
// классов binmatrix и/или binspace
// все необходимые структуры передавать ссылками в качестве параметров 
	tmp.convert();
	tmp.spec();
	t_src p=(t_src)0 ;

	if(!(flag&HAVE_A))
	{
		A=new t_len[n+1];
		flag^=HAVE_A;
	}

	A[0]=1;
	for(t_len a=1; a<n+1; a++)
			A[a]=0;

	for(t_cnt kk=0; kk<=n; kk++){
		for(t_cnt i=0; i<=n; i++) {
			
		t_res sgn=1 ;
			p=(t_src)0 ;
		for(t_cnt j=0; j<=kk; j++){
				if ((i>=j) && ((n-i)>=(kk-j))){
					if (sgn)
						p+= C[i][j]*C[n-i][kk-j] ;
					else
						p-= C[i][j]*C[n-i][kk-j] ;
				}
				sgn = (sgn) ? 0 : 1 ;
		 }
			
		A[kk] += tmp.A[i]*p ;
		}
		A[kk]=A[kk]>>tmp.k;
	}
} 


